import React from 'react';

import LoadMore from './LoadMore';
import NewCount from './NewCount';
import {TransitionGroup} from 'react-transition-group';
import {forEachError} from 'coral-framework/utils';
import Comment from '../containers/Comment';

const hasComment = (nodes, id) => nodes.some((node) => node.id === id);

// resetCursors will return the id cursors of the first and second comment of
// the current comment list. The cursors are used to dertermine which
// comments to show. The spare cursor functions as a backup in case one
// of the comments gets deleted.
function resetCursors(state, props) {
  const comments = props.root.asset.comments;
  if (comments && comments.nodes.length) {
    const idCursors = [comments.nodes[0].id];
    if (comments.nodes[1]) {
      idCursors.push(comments.nodes[1].id);
    }
    return {idCursors};
  }
  return {idCursors: []};
}

// invalidateCursor is called whenever a comment is removed which is referenced
// by one of the 2 id cursors. It returns a new set of id cursors calculated
// using the help of the backup cursor.
function invalidateCursor(invalidated, state, props) {
  const alt = invalidated === 1 ? 0 : 1;
  const comments = props.root.asset.comments;
  const idCursors = [];
  if (state.idCursors[alt]) {
    idCursors.push(state.idCursors[alt]);
    const index = comments.nodes.findIndex((node) => node.id === idCursors[0]);
    const nextInLine = comments.nodes[index + 1];
    if (nextInLine) {
      idCursors.push(nextInLine.id);
    }
  }
  return {idCursors};
}

class AllCommentsPane extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      ...resetCursors(this.state, props),
      loadingState: '',
    };
  }

  componentWillReceiveProps(next) {
    const {comments: prevComments} = this.props;
    const {comments: nextComments} = next;

    if (!prevComments && nextComments) {
      this.setState(resetCursors);
      return;
    }

    if (
      prevComments && nextComments &&
        nextComments.nodes.length < prevComments.nodes.length
    ) {

      // Invalidate first cursor if referenced comment was removed.
      if (this.state.idCursors[0] && !hasComment(nextComments.nodes, this.state.idCursors[0])) {
        this.setState(invalidateCursor(0, this.state, next));
      }

      // Invalidate second cursor if referenced comment was removed.
      if (this.state.idCursors[1] && !hasComment(nextComments.nodes, this.state.idCursors[1])) {
        this.setState(invalidateCursor(1, this.state, next));
      }
    }
  }

  loadMore = () => {
    this.setState({loadingState: 'loading'});
    this.props.loadMore()
      .then(() => {
        this.setState({loadingState: 'success'});
      })
      .catch((error) => {
        this.setState({loadingState: 'error'});
        forEachError(error, ({msg}) => {this.props.notify('error', msg);});
      });
  }

  viewNewComments = () => {
    this.setState(resetCursors);
    this.props.emit('ui.AllCommentsPane.viewNewComments');
  };

  // getVisibileComments returns a list containing comments
  // which were authored by current user or comes after the `idCursor`.
  getVisibleComments() {
    const {comments, currentUser: user} = this.props;
    const idCursor = this.state.idCursors[0];
    const userId = user ? user.id : null;

    if (!comments) {
      return [];
    }

    const view = [];
    let pastCursor = false;
    comments.nodes.forEach((comment) => {
      if (comment.id === idCursor) {
        pastCursor = true;
      }
      if (pastCursor || comment.user.id === userId) {
        view.push(comment);
      }
    });
    return view;
  }

  render() {
    const {
      data,
      root,
      comments,
      commentClassNames,
      setActiveReplyBox,
      activeReplyBox,
      notify,
      disableReply,
      postComment,
      asset,
      currentUser,
      postFlag,
      postDontAgree,
      loadNewReplies,
      deleteAction,
      showSignInDialog,
      charCountEnable,
      maxCharCount,
      editComment,
      emit,
    } = this.props;

    const {loadingState} = this.state;
    const view = this.getVisibleComments();

    return (
      <div className="talk-stream-comments-container">
        <NewCount
          count={comments.nodes.length - view.length}
          loadMore={this.viewNewComments}
        />
        <TransitionGroup component='div' className="embed__stream">
          {view.map((comment) => {
            return (
              <Comment
                commentClassNames={commentClassNames}
                data={data}
                root={root}
                disableReply={disableReply}
                setActiveReplyBox={setActiveReplyBox}
                activeReplyBox={activeReplyBox}
                notify={notify}
                depth={0}
                postComment={postComment}
                asset={asset}
                currentUser={currentUser}
                postFlag={postFlag}
                postDontAgree={postDontAgree}
                loadMore={loadNewReplies}
                deleteAction={deleteAction}
                showSignInDialog={showSignInDialog}
                key={comment.id}
                comment={comment}
                charCountEnable={charCountEnable}
                maxCharCount={maxCharCount}
                editComment={editComment}
                emit={emit}
              />
            );
          })}
        </TransitionGroup>
        <LoadMore
          topLevel={true}
          moreComments={asset.comments.hasNextPage}
          loadMore={this.loadMore}
          loadingState={loadingState}
        />
      </div>
    );
  }
}

export default AllCommentsPane;
